package com.bluemoon.daps.test.sync.v1_3.model2prod;

import cn.com.bluemoon.daps.common.domain.UserInfoHolder;
import cn.com.bluemoon.daps.common.enums.DatabaseType;
import cn.com.bluemoon.daps.common.enums.SyncStatus;
import cn.com.bluemoon.daps.common.enums.SyncToEnv;
import cn.com.bluemoon.daps.common.exception.DapThrowException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.common.toolkit.SshUtils;
import cn.com.bluemoon.daps.model.entity.DapDataModelTable;
import cn.com.bluemoon.daps.sync.DapsSyncApplication;
import cn.com.bluemoon.daps.sync.base.BmScript;
import cn.com.bluemoon.daps.sync.base.CompareResult;
import cn.com.bluemoon.daps.sync.base.ModifyType;
import cn.com.bluemoon.daps.sync.base.SqlCompareResult;
import cn.com.bluemoon.daps.sync.core.sync.GlobalConfig;
import cn.com.bluemoon.daps.sync.core.sync.TableModel;
import cn.com.bluemoon.daps.sync.core.sync.compare.ModelCompareClient;
import cn.com.bluemoon.daps.sync.core.sync.compare.TableCompareRes;
import cn.com.bluemoon.daps.sync.core.sync.executor.SqlExecutor;
import cn.com.bluemoon.daps.sync.core.sync.executor.SqlExecutors;
import cn.com.bluemoon.daps.sync.core.sync.sql.mysql.MysqlConnect;
import cn.com.bluemoon.daps.sync.core.sync.sql.mysql.MysqlDump;
import cn.com.bluemoon.daps.sync.core.sync.sql.mysql.MysqlExec;
import cn.com.bluemoon.daps.sync.core.sync.sql.mysql.MysqlImport;
import cn.com.bluemoon.daps.sync.entity.DapSyncProdEnvTask;
import cn.com.bluemoon.daps.sync.entity.DapSyncProdEnvTaskDetail;
import cn.com.bluemoon.daps.sync.service.DapSyncBasicService;
import cn.com.bluemoon.daps.sync.service.DapSyncProdEnvTaskDetailService;
import cn.com.bluemoon.daps.sync.service.DapSyncProdEnvTaskService;
import cn.com.bluemoon.daps.sync.vo.model2prod.SyncProTaskDto;
import cn.com.bluemoon.daps.system.entity.DapSystemConfigSandbox;
import cn.com.bluemoon.daps.system.entity.DapSystemDatasourceEnvironment;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.bluemoon.daps.test.sync.v1_3.devimport.BaseBefore;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.com.bluemoon.daps.sync.core.sync.log.SyncEnvProdLog.getTaskAndDetailId;

/**
 * 预发布
 *
 * @author Jarod.Kong
 * @date 2021/6/2 16:35
 */
@SpringBootTest(classes = DapsSyncApplication.class)
@RunWith(value = SpringRunner.class)
@Slf4j
public class Model2PreProdV1 extends BaseBefore {

    @Autowired
    private DapSyncProdEnvTaskService taskService;

    @Autowired
    private DapSyncProdEnvTaskDetailService taskDetailService;

    @Autowired
    private DapSyncBasicService syncBasicService;

    public Model2PreProdV1() {
        super(SyncToEnv.PRE_PROD);
    }

    /**
     * 重写对比用于发布同步
     *
     * @param globalConfig
     * @param modelV1
     * @param modelV2
     * @return
     */
    public static SyncProTaskDto doComparePublishSync(GlobalConfig globalConfig, List<TableModel> modelV1, List<TableModel> modelV2) {
        List<Map<ModifyType.ModifyLevel, List<CompareResult<SqlCompareResult>>>> compareR = ModelCompareClient
                .doCompare(ModelCompareClient.BizType.SQL, globalConfig, modelV1, modelV2);
        log.info("两个版本对比结果：{}", compareR.size());
        BmAssetUtils.isFalse(compareR.isEmpty(), "对比无变化无需生成发布同步数据");
        DapSyncProdEnvTask task = new DapSyncProdEnvTask();
        // TODO: 2021/6/25 调整为申请发布的人
        task.setChangeOrder(IdWorker.get32UUID());
        task.setApplicationBy("申请者");
        task.setVerifyBy("审核者");
        task.setChangeDesc("发布新版本");
        task.setModelId(globalConfig.getModel().getId());
        task.setDataSourceId(globalConfig.getDataSource().getId());
        task.setPreProdStatus(SyncStatus.SYNC_WAIT);
        task.setProdStatus(SyncStatus.SYNC_WAIT);
        task.setId(IdWorker.getIdStr());
        List<DapSyncProdEnvTaskDetail> details = compareR.stream().map(tableInfo -> {
            List<CompareResult<SqlCompareResult>> compareResults = tableInfo.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
            List<DapSyncProdEnvTaskDetail> taskDetails = compareResults.stream().map(c -> {
                SqlCompareResult result = c.getData();
                DapSyncProdEnvTaskDetail taskDetail = new DapSyncProdEnvTaskDetail();
                taskDetail.setId(IdWorker.getIdStr());
                taskDetail.setModelId(c.getModelId());
                taskDetail.setTableId(c.getTableId());
                taskDetail.setTableName(c.getTableName());
                taskDetail.setTaskId(task.getId());
                // 收集表级别的变更ddl
                if (c.getModifyLevel().equals(ModifyType.ModifyLevel.TABLE)) {
                    taskDetail.setPtDdl(result.getScript().getSql());
                }
                taskDetail.setDdl(result.getScript().getSql());
                taskDetail.setPtOsc(result.getScript().getPtSql());
                taskDetail.setPreProdScriptStatus(SyncStatus.SYNC_WAIT);
                taskDetail.setProdScriptStatus(SyncStatus.SYNC_WAIT);
                return taskDetail;
            }).collect(Collectors.toList());
            // 对一个表多个变更的组合
            // 1)只有一个变更直接把ddl
            if (taskDetails.size() == 1) {
                return taskDetails.get(0);
            }
            // 2)多个变更合并
            DapSyncProdEnvTaskDetail taskDetail = taskDetails.stream().reduce((d1, d2) -> {
                // 组合可执行的sql+ptosc
                if (d2.getPtDdl() != null && d2.getPtOsc() == null) {
                    String ptDdl = Optional.ofNullable(d1.getPtDdl()).map(d -> concatAndLine(d, ";").concat(d2.getPtDdl())).orElse(d2.getPtDdl());
                    d1.setPtDdl(ptDdl);
                } else {
                    String ptOsc = Optional.ofNullable(d1.getPtOsc()).map(d -> concatAndLine(d, ",").concat(d2.getPtOsc())).orElse(d2.getPtOsc());
                    d1.setPtOsc(ptOsc);
                }
                // 收集以上比较出来的d1#ddl结果
                String ddl = Optional.ofNullable(d1.getDdl()).map(d -> concatAndLine(d, ";").concat(d2.getDdl()))
                        .orElseGet(() -> {
                            List<String> res = Stream.of(d1.getDdl(), d2.getDdl()).filter(StringUtils::isNotBlank).collect(Collectors.toList());
                            return String.join(concatAndLine("", ";"), res);
                        });
                d1.setDdl(ddl);
                return d1;
            }).orElseThrow(() -> new DapThrowException("合并表中多个变更记录失败！！"));
            return taskDetail;
        }).collect(Collectors.toList());
        SyncProTaskDto syncProTaskDto = SyncProTaskDto.builder().task(task).taskDetails(details).build();
        return syncProTaskDto;
    }

    private static String concatAndLine(String str, String dom) {
        if (str == null) {
            return "";
        }
        return str.concat(dom).concat(System.lineSeparator());
    }

    /**
     * 获取申请发布写入到{@link cn.com.bluemoon.daps.sync.entity.DapSyncProdEnvTask}
     * 和{@link cn.com.bluemoon.daps.sync.entity.DapSyncProdEnvTaskDetail}中的数据
     * 模型单号->每个表的ddl变更记录
     * 分页查询
     */
    @Test
    public void test_sync_list_page() {

    }

    /**
     * 点击列表行数据获取操作日志列表
     */
    @Test
    public void test_click_sync_list_query_operation_list() {

    }

    /**
     * 点击同步预发布
     * 获取模型单号对应的同步信息
     * {@link cn.com.bluemoon.daps.sync.entity.DapSyncProdEnvTask}
     * {@link cn.com.bluemoon.daps.sync.entity.DapSyncProdEnvTaskDetail}
     * 的组合数据，只能查看，不可编辑
     */
    @Test
    public void test_click_sync_pre_prod_info() {

    }

    @Test
    public void test_model_compare_preprod() {
        // 构造模型版本数据
        Tuple2<List<TableModel>, List<TableModel>> m = getV12Model(true);
        List<TableModel> modelV1 = m.getT1();
        List<TableModel> modelV2 = m.getT2();
        SyncProTaskDto syncProTaskDtos = doComparePublishSync(globalConfig, modelV1, modelV2);
        log.info("对比结果：单号={}，变更数={}", syncProTaskDtos.getTask().getChangeOrder(),
                syncProTaskDtos.getTaskDetails().size());
    }

    /**
     * @param mock true 针对获取的两个版本可能不存在差异的情况下，直接构造所有属性都发生变化 ，false取真实数据
     * @return 结果集
     */
    private Tuple2<List<TableModel>, List<TableModel>> getV12Model(boolean mock) {
        globalConfig.setModelNewest(false);
        List<TableModel> localAndMetadataTableModel = getLocalTableModel()
                .stream()
                .peek(t -> {
                    DapDataModelTable table = t.getTable();
                    // v1拿pid对应到最新模型id
                    table.setBmKey(table.getPId());
                    t.getFields().forEach(f -> f.setBmKey(f.getPId()));
                    t.getIndexes().forEach(i -> i.setBmKey(i.getPId()));
                    t.getPartitions().forEach(p -> p.setBmKey(p.getPId()));
                }).collect(Collectors.toList());
        // 模拟模型v1数据
        List<TableModel> modelV1 = localAndMetadataTableModel;
        // 模拟模想v2数据
        globalConfig.setModelNewest(true);
        List<TableModel> modelV2 = getLocalTableModel()
                .stream()
                .peek(t -> {
                    DapDataModelTable table = t.getTable();
                    table.setBmKey(table.getId());
                    t.getFields().forEach(f -> f.setBmKey(f.getId()));
                    t.getIndexes().forEach(i -> i.setBmKey(i.getId()));
                    t.getPartitions().forEach(p -> p.setBmKey(p.getId()));
                })
                .peek(t -> {
                    // 模拟版本差异的生成
                    if (mock) {
                        String suffix = IdWorker.getIdStr();
                        String name = t.getTable().getName() + "_" + suffix;
                        t.getTable().setName(name);
                        // 增加修改表英文名 当前对比基于id，不是表英文名
                        String engName = t.getTable().getEngName() + "_" + suffix;
                        t.getTable().setEngName(engName);
                        t.getFields().forEach(i -> {
                            i.setComment(i.getComment() + "_" + suffix);
                        });
                        t.getIndexes().forEach(i -> {
                            i.setKeyChinName(i.getKeyChinName() + "_" + suffix);
                        });
                    }
                }).collect(Collectors.toList());
        return Tuples.of(modelV1, modelV2);
    }


    /**
     * 保证
     * 重建当前环境的数据库与模型上一个版本一致
     * * 1.模型版本数据对比预发布数据库结构，
     * step 1
     */
    @Test
    public void test_dev2model_and_mode2dev_sql_2DB() {
        globalConfig.setModelNewest(false);// 拿最新已发布的放到线上
        Tuple2<List<TableModel>, List<TableModel>> localAndMetadataTableModel = getLocalAndMetadataTableModel();
        List<TableModel> modelV1 = localAndMetadataTableModel.getT1();
        List<TableModel> modelV2 = localAndMetadataTableModel.getT2();
        globalConfig.setCheckSqlLayer(true);
        globalConfig.setCanDropDbTable(true);// 保证模型与数据库内容一致
        List<Map<ModifyType.ModifyLevel, List<CompareResult<SqlCompareResult>>>> compareR = ModelCompareClient
                .doCompare(ModelCompareClient.BizType.SQL, globalConfig, modelV2, modelV1);
        log.info("模型导入开发库（开发同步）：{}, size={}", compareR, compareR.size());
        // 根据模型对应的数据源中的开关（是否开启ptosc同步）
        Boolean isPtosc = globalConfig.getDataSource().getIsPtosc();
        SqlExecutor.ExecutorType type;
        if (isPtosc == null || Boolean.TRUE.equals(isPtosc)) {
            type = SqlExecutor.ExecutorType.PT;
        } else {
            type = SqlExecutor.ExecutorType.JDBC;
        }
        // 异步提交等待返回
        SqlExecutor sqlExecutor = SqlExecutors.use(type, globalConfig).async().sqlResult(compareR);
        boolean logInfoCollectors = sqlExecutor.execute();
        System.out.println(logInfoCollectors);
    }

    /**
     * 申请发布
     * <p>
     * 对比最新模型数据与上一个版本数据的差异数据
     * 入库到发布同步中
     * step 2
     */
    @Test
    public void test_get_apply_publish_data_2DB() {
        // 入库申请发布数据
        // 增加当前模型是否存在未同步完成的数据，有则不允许新增 // TODO: 2021/6/30 根据模型id查看是否存在待同步数据，
        Tuple2<List<TableModel>, List<TableModel>> m = getV12Model(false);
        List<TableModel> modelV1 = m.getT1();
        List<TableModel> modelV2 = m.getT2();
        globalConfig.setCanDropDbTable(true);
        SyncProTaskDto syncProTaskDtos = doComparePublishSync(globalConfig, modelV1, modelV2);
        log.info("对比结果：单号={}，变更数={}", syncProTaskDtos.getTask().getChangeOrder(),
                syncProTaskDtos.getTaskDetails().size());
        // 66fb47dc70e1320d494bf2e0f2949ec8
        DapSyncProdEnvTask task = syncProTaskDtos.getTask();
        List<DapSyncProdEnvTaskDetail> taskDetails = syncProTaskDtos.getTaskDetails();
        boolean b = taskService.save(task);
        boolean b1 = taskDetailService.bmSaveBatch(taskDetails);
        log.info("入库任务明细：最新模型任务为taskId = {}, size={}, status={}", task.getId(), taskDetails.size(), b1);
    }


    /**
     * 点击执行同步预发布
     * step 3
     */
    @Test
    public void test_click_exec_sync_pre_prod_() {
        // 根据申请发布结果集，对每个表生成ptosc命令或ddl命令
        // 执行器执行命令
        // 执行器输出日志+发送消息（邮件）
        // 1）先拉取预发布环境的数据库ddl语句mysqldump到服务器磁盘上
        // 2）获取同步的沙箱环境连接信息，执行导入mysqldump文件到沙箱中
        // 3）执行器在沙箱环境上执行命令
        // 4）若执行成功，则进行执行器在预发布环境上执行命令；若失败则再次从1）-3），直到成功为止
        // 5）若执行成功，则通知、修改同步状态等结束；若失败则再次从1）-4），直到成功为止
        // 同步生产与预发布类似
        String taskId = "1410474733469753345";
        // TODO: 2021/6/30 增加模型中任务状态为 同步失败、未同步 可以执行
        DapSyncProdEnvTask task = taskService.getByIdAsset(taskId);
        if (SyncToEnv.PRE_PROD.equals(globalConfig.getSyncToEnv())) {
            BmAssetUtils.isTrue(SyncStatus.SYNC_WAIT.equals(task.getPreProdStatus())
                    || SyncStatus.SYNC_ERROR.equals(task.getPreProdStatus()), "非待同步任务");
        }
        if (SyncToEnv.PROD.equals(globalConfig.getSyncToEnv())) {
            BmAssetUtils.isTrue(SyncStatus.SYNC_WAIT.equals(task.getProdStatus())
                    || SyncStatus.SYNC_ERROR.equals(task.getPreProdStatus()), "非待同步任务");
        }
        // 执行前修改为同步中
        task.setSyncStatus(SyncStatus.SYNC_DOING, globalConfig.getSyncToEnv());
        task.setSyncTime(LocalDateTime.now(), globalConfig.getSyncToEnv());
        task.setSyncBy(UserInfoHolder.getAccount(), globalConfig.getSyncToEnv());
        boolean updateStatus = task.updateById();
        log.info("修改任务单号状态为{}, status:{}", SyncStatus.SYNC_DOING, updateStatus);
        rebuildSandboxEnv();
        boolean enabled = doSyncOnSandbox(taskId);
        // 必须沙箱成功才可以真正同步
        if (!enabled) {
            // 修改单号状态为 同步失败；而单号ddl数据由cn.com.bluemoon.daps.sync.service.impl.DapModel2ProdSyncServiceImpl.syncEnvLogEventHandler维护
            task.setSyncStatus(SyncStatus.SYNC_ERROR, globalConfig.getSyncToEnv());
            task.setSyncTime(LocalDateTime.now(), globalConfig.getSyncToEnv());
            boolean status = task.updateById();
            log.warn("同步{}中沙箱环境执行失败，更新单号{}数据为{}", globalConfig.getSyncToEnv().getDesc(), taskId, status);
            // 2021/7/1 发送失败邮件
            syncBasicService.sendEmailPublishSync(task, globalConfig);
//            BmAssetUtils.isTrue(enabled, "脚本在沙箱环境中执行失败，不继续同步到" + globalConfig.getSyncToEnv().getDesc() + "环境中");
        } else {
            boolean b = doSyncOnPreProd(taskId);
            log.info("最终同步情况：{}", b);
        }
    }

    /**
     * 同步结果验证：
     * 拿当前模型最新的数据比较预发布的，
     * 要是没有变化说明同步正确
     */
    @Test
    public void test_check_sync_pre_prod_result() {
        globalConfig.setModelNewest(true);// 拿最新已发布的放到线上
        Tuple2<List<TableModel>, List<TableModel>> localAndMetadataTableModel = getLocalAndMetadataTableModel();
        List<TableModel> modelV1 = localAndMetadataTableModel.getT1();
        List<TableModel> modelV2 = localAndMetadataTableModel.getT2();
        globalConfig.setCheckSqlLayer(true);
        List<Map<ModifyType.ModifyLevel, List<CompareResult<SqlCompareResult>>>> compareR = ModelCompareClient
                .doCompare(ModelCompareClient.BizType.SQL, globalConfig, modelV2, modelV1);
        log.info("模型导入开发库（开发同步）：{}, size={}", compareR, compareR.size());
        // 根据模型对应的数据源中的开关（是否开启ptosc同步）
        // 异步提交等待返回
        BmAssetUtils.isTrue(compareR.isEmpty(), "对比结果为空，最新模型数据已同步到预发布环境");
    }

    /**
     * 同步结果验证：
     * 拿当前模型最新的数据比较预发布的，
     * 要是没有变化说明同步正确
     */
    @Test
    public void test_check_pre_prod_result_equal_sandbox_result() {
        List<TableModel> v1 = getMetadataTableModel();
        globalConfig.setSyncSandboxMock(true);
        List<TableModel> v2 = getMetadataTableModel();
        Tuple2<List<TableModel>, List<TableModel>> localAndMetadataTableModel = getLocalAndMetadataTableModel();
        List<TableModel> modelV1 = localAndMetadataTableModel.getT1();
        List<TableModel> modelV2 = localAndMetadataTableModel.getT2();
        globalConfig.setCheckSqlLayer(true);
        List<Map<ModifyType.ModifyLevel, List<CompareResult<SqlCompareResult>>>> compareR = ModelCompareClient
                .doCompare(ModelCompareClient.BizType.SQL, globalConfig, modelV2, modelV1);
        log.info("模型导入开发库（开发同步）：{}, size={}", compareR, compareR.size());
        // 根据模型对应的数据源中的开关（是否开启ptosc同步）
        // 异步提交等待返回
        BmAssetUtils.isTrue(compareR.isEmpty(), "对比结果为空，最新模型数据已同步到预发布环境");
    }

    private boolean doSyncOnPreProd(String taskId) {
        globalConfig.setSyncSandboxMock(false);
        return doSync0(taskId);
    }

    private boolean doSyncOnSandbox(String taskId) {
        globalConfig.setSyncSandboxMock(true);
        return doSync0(taskId);
    }

    private void rebuildSandboxEnv() {
        test_drop_sandbox_env_database();
        test_import_dump_file_to_sandbox_env();
    }

    @Test
    public void test_do_sync0() {
        doSync0("1409757533830172673");
    }

    private boolean doSync0(String taskId) {
        DapSyncProdEnvTask task = taskService.getByIdAsset(taskId, "同步任务不存在");
        // 增加逻辑 每次执行都是拿那些非已同步的脚本去跑，因为已同步的不用再次同步了
        List<DapSyncProdEnvTaskDetail> taskDetails = new ArrayList<>();
        LambdaQueryWrapper<DapSyncProdEnvTaskDetail> eq = null;
        // 2021/7/1 获取同步数据中，要是为走 isSyncSandboxMock的情况下，都是那全量ddl进行沙箱环境执行
        if (SyncToEnv.PRE_PROD.equals(globalConfig.getSyncToEnv())) {
            eq = taskDetailService.bmLambdaQuery()
                    .eq(DapSyncProdEnvTaskDetail::getTaskId, taskId)
                    .ne(!globalConfig.isSyncSandboxMock(), DapSyncProdEnvTaskDetail::getPreProdScriptStatus, SyncStatus.SYNC_DONE);
            taskDetails = taskDetailService.list(eq);
        }
        if (SyncToEnv.PROD.equals(globalConfig.getSyncToEnv())) {
            eq = taskDetailService.bmLambdaQuery()
                    .eq(DapSyncProdEnvTaskDetail::getTaskId, taskId)
                    .ne(!globalConfig.isSyncSandboxMock(), DapSyncProdEnvTaskDetail::getProdScriptStatus, SyncStatus.SYNC_DONE);
            taskDetails = taskDetailService.list(eq);
        }
        List<TableCompareRes<SqlCompareResult>> res = taskDetails.stream()
                .filter(t -> t.getPtDdl() != null || t.getPtOsc() != null) // 要是没脚本则忽略
                .flatMap((Function<DapSyncProdEnvTaskDetail, Stream<TableCompareRes<SqlCompareResult>>>) d -> {
                    List<TableCompareRes<SqlCompareResult>> tableCompareRes = new ArrayList<>();
                    final String taskAndDetailId = getTaskAndDetailId(d);
                    // 要是data存在ptddl则执行ptddl，后执行ptOsc => 调整为ptddl生成独立的CompareResult、ptosc生成独立的CompareResult
                    List<CompareResult<SqlCompareResult>> compareResults = new ArrayList<>(2);
                    if (d.getPtDdl() != null) {
                        BmScript bmScript = BmScript.builder().sql(d.getPtDdl()).ptSql(null).build();
                        SqlCompareResult c = SqlCompareResult.builder().script(bmScript).build();
                        CompareResult<SqlCompareResult> result = CompareResult.<SqlCompareResult>builder()
                                .modelId(d.getModelId())
                                .tableName(d.getTableName())
                                .tableId(d.getTableId())
                                .modifyType(null)// 无法确定，给null不影响后续ptosc的脚本执行
                                .modifyLevel(ModifyType.ModifyLevel.TABLE)// 2021年6月28日 区分表级脚本还是非表级
                                .data(c)
                                .build();
                        compareResults.add(result);
                    }
                    if (d.getPtOsc() != null) {
                        BmScript bmScript = BmScript.builder().sql(null).ptSql(d.getPtOsc()).build();
                        SqlCompareResult c = SqlCompareResult.builder().script(bmScript).build();
                        CompareResult<SqlCompareResult> result = CompareResult.<SqlCompareResult>builder()
                                .modelId(d.getModelId())
                                .tableName(d.getTableName())
                                .tableId(d.getTableId())
                                .data(c)
                                .build();
                        compareResults.add(result);
                    }
                    TableCompareRes<SqlCompareResult> t1 = TableCompareRes.<SqlCompareResult>builder()
                            .globalConfig(globalConfig)
                            .compareResults(compareResults)
                            .tableInfo(compareResults.get(0))//前置条件必定至少一个
                            .uniqueKey(taskAndDetailId)
                            .build();
                    tableCompareRes.add(t1);
                    return tableCompareRes.stream();
                }).collect(Collectors.toList());
        return SqlExecutors.pt(globalConfig).async().sqlResult0(res).execute();
    }


    /**
     * 查看执行预发布同步的日志（区分预发布和生产环境）
     * 注：获取模型单号对应的所有变更表的日志结果进行汇总（合并）
     */
    @Test
    public void test_click_view_sync_pre_prod_execute_log() {

    }

    /**
     * 同步列表中查看模型单号下的执行日志（区分预发布和生产环境）
     */
    @Test
    public void test_click_sync_list_view_log() {
        test_click_view_sync_pre_prod_execute_log();
    }


    /**
     * 对沙箱环境进行删除+创建数据库
     */
    @Test
    public void test_drop_sandbox_env_database() {
        String dbname = "data_asset_platform";
        DapSystemConfigSandbox dsSandboxEnv = globalConfig.getDsSandboxEnv();
        MysqlConnect mysqlConnect = MysqlConnect.builder().scriptPath("/usr/bin/mysql")
                .host(dsSandboxEnv.getHost()).port(dsSandboxEnv.getPort()).user(dsSandboxEnv.getUsername()).password(dsSandboxEnv.decryptPassword()).build();
        MysqlExec mysqlExec = MysqlExec.builder().mysqlCommand("drop database if exists " + dbname).mysqlConnect(mysqlConnect).build();
        String res = SshUtils.client().runScriptAsset(mysqlExec.toString());
        Assert.assertEquals(res, "");
        mysqlExec = MysqlExec.builder().mysqlCommand("create database if not exists " + dbname).mysqlConnect(mysqlConnect).build();
        res = SshUtils.client().runScriptAsset(mysqlExec.toString());
        Assert.assertEquals(res, "");
    }

    @Test
    public void test_import_dump_file_to_sandbox_env() {
        // 同步沙箱导入到的数据库前，删除历史的表和数据库名
        DapSystemDatasourceEnvironment ds = globalConfig.getDataSourceEnv();
        String outputPath = "/data/ptadmin/sandbox-script/mysql_dump_" + syncToEnv.name().toLowerCase() + "_" + ds.getDatabaseName() + "_ddl.sql";
        MysqlDump mysqlDump = MysqlDump.builder().scriptPath("/usr/bin/mysqldump")
                .user(ds.getUsername())
                .password(ds.decryptPassword())
                .host(ds.getHost()).port(ds.getPort())
                .database(ds.getDatabaseName())
                .resultFile(outputPath).build();
        String res = SshUtils.client().runScriptAsset(mysqlDump.toString());
        log.info("dump:=>{}", mysqlDump.toString(true));
        Assert.assertEquals(res, "");
        String dumpFile = outputPath;
        // /usr/bin/mysql -h192.168.234.4 -P3303 -uds_sync_struct -pJmMBtXTz < /data/ptadmin/sandbox-script/mysql_dump_DEV_data_asset_platform_ddl.sql
        DapSystemConfigSandbox dsSandboxEnv = globalConfig.getDsSandboxEnv();
        String url = dsSandboxEnv.getUrl(DatabaseType.MYSQL, ds.getDatabaseName());
        log.info("沙箱环境的url:{}", url);
        MysqlImport mysqlImport = MysqlImport.builder().mysqlConnect(
                MysqlConnect.builder().scriptPath("/usr/bin/mysql")
                        .host(dsSandboxEnv.getHost()).port(dsSandboxEnv.getPort()).user(dsSandboxEnv.getUsername()).password(dsSandboxEnv.decryptPassword()).build())
//                        .host("192.168.234.4").port(3309).user("ds_sync_struct").password("JmMBtXTz").build())
                .importFilePath(dumpFile).build();
        String script = mysqlImport.toString();
        String s = SshUtils.client().runScriptAsset(script);
        System.out.println(s);
    }



}
